use std::collections::BTreeMap;

use anyhow::Result;
use base64::prelude::*;
use rsa::pkcs1v15::{Signature, SigningKey, VerifyingKey};
use rsa::pkcs8::DecodePrivateKey;
use rsa::pkcs8::DecodePublicKey;
use rsa::signature::{RandomizedSigner, SignatureEncoding, Verifier};
use rsa::{RsaPrivateKey, RsaPublicKey};
use sha1::Sha1;

use crate::utils;

#[derive(Debug, Clone)]
pub struct MchCredential {
    /// 通联支付分配的商户号
    pub cusid: String,
    /// 通联支付分配给商户的 APPID
    pub appid: String,
    /// 微信小程序/微信公众号/APP的appid
    pub sub_appid: String,
    /// 签名方式：RSA/SM2
    pub signtype: String,
    /// 商户 RSA 私钥
    pub mch_rsa_private_key: RsaPrivateKey,
    /// 通联平台 RSA 公钥
    pub tonglian_rsa_public_key: RsaPublicKey,
}
impl MchCredential {
    // parse rsa private key from pkcs8 string
    pub fn private_key_from_pkcs8_str(mch_private_key: &str) -> Result<RsaPrivateKey> {
        let der_key = BASE64_STANDARD.decode(mch_private_key)?;
        let private_key = RsaPrivateKey::from_pkcs8_der(&der_key)?;
        Ok(private_key)
    }
    // parse rsa public key
    pub fn public_key_from_str(tonglian_public_key: &str) -> Result<RsaPublicKey> {
        let der_key = BASE64_STANDARD.decode(tonglian_public_key)?;
        let public_key = RsaPublicKey::from_public_key_der(&der_key)?;
        Ok(public_key)
    }

    pub fn new(
        cusid: String,
        appid: String,
        sub_appid: String,
        mch_rsa_private_key: RsaPrivateKey,
        tonglian_rsa_public_key: RsaPublicKey,
    ) -> MchCredential {
        MchCredential {
            cusid,
            appid,
            sub_appid,
            signtype: "RSA".to_string(),
            mch_rsa_private_key,
            tonglian_rsa_public_key,
        }
    }

    // sign plain text
    pub fn sign(&self, plain: &str) -> String {
        let mut rng = rand::thread_rng();
        let signer = SigningKey::<Sha1>::new(self.mch_rsa_private_key.clone());
        let signature = signer.sign_with_rng(&mut rng, plain.as_bytes()).to_bytes();
        BASE64_STANDARD.encode(&signature)
    }
    // verify signature
    fn verify(&self, plain: &str, signature: &str) -> Result<()> {
        let signature = BASE64_STANDARD.decode(signature)?;
        let signature = Signature::try_from(signature.as_slice())?;
        let verifier = VerifyingKey::<Sha1>::new(self.tonglian_rsa_public_key.clone());
        verifier.verify(plain.as_bytes(), &signature)?;
        Ok(())
    }
    // verify response signature
    pub fn verify_response(&self, response: &mut BTreeMap<String, String>) -> Result<()> {
        let sign = response.remove("sign").unwrap();
        let plain = utils::btree_map_to_query_string(response);
        self.verify(&plain, &sign)?;
        Ok(())
    }
}
